package com.clp.protocol.iec104.server;

import com.clp.protocol.iec104.definition.Tc;
import com.clp.protocol.core.utils.AssertUtil;
import lombok.Getter;

import java.util.Arrays;
import java.util.Objects;

public class TcToken implements CtrlToken {

    private enum State {
        R_SELECT,
        S_SELECT_ACK_YES,
        S_SELECT_ACK_NO,
        R_EXECUTE,
        S_EXECUTE_ACK_YES,
        S_EXECUTE_ACK_NO,
        S_FINISHED
    }

    @Getter
    private final int address;

    @Getter
    private volatile SlaveChannel applier; // 当前的申请者
    private volatile State state;
    @Getter
    private volatile Tc.Type type;
    private volatile boolean isSwitchOn;

    public TcToken(int addr) {
        this.address = addr;
    }

    void setApplier(SlaveChannel newApplier) {
        // 要求当前没有申请者
        if (applier != null) {
            throw new IllegalStateException("当前已经有了一个申请者：" + applier);
        }
        AssertUtil.notNull(newApplier, "slaveChannel");
        applier = newApplier;
        resetState();
    }

    private void resetState() {
        state = State.S_FINISHED;
    }

    void removeApplier() {
        applier = null;
    }

    /**
     * 设置为接受到遥控选择
     * @param isSwitchOn
     */
    public void setRSelect(Tc.Type type, boolean isSwitchOn) {
        checkStateIn(State.S_FINISHED); // 校验
        this.state = State.R_SELECT;
        this.type = type; // 遥控类型
        this.isSwitchOn = isSwitchOn;
    }

    private void checkStateIn(State... states) {
        for (State stateToCheck : states) {
            if (state == stateToCheck) return;
        }
        throw new IllegalStateException("当前状态：" + state + "，需要状态：" + Arrays.toString(states));
    }

    public boolean isRSelect() {
        return state == State.R_SELECT;
    }

    public void setSSelectAck(Tc.Type type, boolean isAckYes) {
        checkType(type);
        checkStateIn(State.R_SELECT);
        if (isAckYes) {
            this.state = State.S_SELECT_ACK_YES;
        } else {
            this.state = State.S_SELECT_ACK_NO;
        }
    }

    private void checkType(Tc.Type typeToCheck) {
        if (type != typeToCheck) {
            throw new IllegalStateException("当前遥控点类型：" + type + "，需要点类型：" + typeToCheck);
        }
    }

    public boolean isSSelectAck(boolean isAckYes) {
        if (isAckYes) return state == State.S_SELECT_ACK_YES;
        return state == State.S_SELECT_ACK_NO;
    }

    public void setRExecute(Tc.Type type, boolean isSwitchOn) {
        checkStateIn(State.S_SELECT_ACK_YES);
        checkType(type);
        checkSwitch(isSwitchOn);
        this.state = State.R_EXECUTE;
    }

    public boolean isRExecute() {
        return state == State.R_EXECUTE;
    }

    private void checkSwitch(boolean isSwitchOn) {
        if (this.isSwitchOn != isSwitchOn) {
            throw new IllegalStateException("当前遥控点开关状态：" + type + "，需要点开关状态：" + isSwitchOn);
        }
    }

    public void setSExecuteAck(Tc.Type type, boolean isAckYes) {
        checkType(type);
        checkStateIn(State.R_EXECUTE);
        if (isAckYes) {
            this.state = State.S_EXECUTE_ACK_YES;
        } else {
            this.state = State.S_EXECUTE_ACK_NO;
        }
    }

    public void setSFinished(Tc.Type type) {
        checkType(type);
        checkStateIn(State.S_EXECUTE_ACK_YES, State.S_EXECUTE_ACK_NO);
        this.state = State.S_FINISHED;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TcToken tcToken = (TcToken) o;
        return address == tcToken.address;
    }

    @Override
    public int hashCode() {
        return Objects.hash(address);
    }
}
